/**************************************************************************************************
**                                                                                               **
**  文件名称:  dal_rtc_drv.c                                                                     **
**  版权所有:  CopyRight @ Xiamen Yaxon NetWork CO.LTD. 2017                                     **
**  文件描述:  RTC实时时钟配置管理                                                               **
**  ===========================================================================================  **
**  创建信息:  | 2017-4-20 | LEON | 创建本模块                                                   **
**  ===========================================================================================  **
**  修改信息:  单击此处添加....                                                                  **
**************************************************************************************************/
#include "dal_include.h"
#include "dal_rtc_drv.h"

/*************************************************************************************************/
/*                           模块宏定义                                                          */
/*************************************************************************************************/
#define RTC_CONFIG           0xAA
#define RTC_WAITIME          0x50000

/*************************************************************************************************/
/*                           定义模块数据结构                                                    */
/*************************************************************************************************/
typedef struct {
    BOOLEAN    inservice;                                                      /* 服务已开启 */
    BOOLEAN    batnormal;                                                      /* 电池状态正常 */
} RTC_DCB_T;

/*************************************************************************************************/
/*                           定义模块变量                                                        */
/*************************************************************************************************/
static RTC_DCB_T s_rtcctrl;

/**************************************************************************************************
**  函数名称:  DAL_RTC_IRQHandler
**  功能描述:  RTC中断事件处理函数
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void DAL_RTC_IRQHandler(void)
{
    if (RTC_GetITStatus(RTC_IT_ALRA) != RESET) {
        RTC_ClearITPendingBit(RTC_IT_ALRA);                                    /* Clear the Alarm A Pending Bit */
        EXTI_ClearITPendingBit(EXTI_Line17);                                   /* Clear EXTI line17 pending bit */
    }  
}

/**************************************************************************************************
**  函数名称:  RTC_PwrConfig
**  功能描述:  电源控制配置
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
static void RTC_PwrConfig(void)
{
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);                        /* 使能PWR 时钟*/
    PWR_BackupAccessCmd(ENABLE);                                               /* 使能RTC和备份域寄存器写访问 */
}

/**************************************************************************************************
**  函数名称:  RCC_ConfigWait
**  功能描述:  配置RCC并等待完成
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
static INT32U RCC_ConfigWait(INT8U flag)
{
    INT32U ct_delay;
    
    ct_delay = 0;
    
    while (++ct_delay < RTC_WAITIME) {                                         /* 等待到LSI 准备就绪 */
        if (RCC_GetFlagStatus(flag) == SET) {
            break;
        }
    }
    
    return ct_delay;
}

/**************************************************************************************************
**  函数名称:  RTC_ClockConfig
**  功能描述:  RTC时钟配置
**  输入参数:  clock:  时钟源, 见 RTC_CLOCK_E
**  输出参数:  无
**  返回参数:  成功返回TRUE,失败返回FALSE
**************************************************************************************************/
static BOOLEAN RTC_ClockConfig(RTC_CLOCK_E clock)
{
    if (clock == RTC_CLOCK_LSI) {                                              /* 当使用LSI 作为RTC 时钟源*/
        RCC_LSICmd(ENABLE);                                                    /* 使能LSI 振荡*/
        RCC_ConfigWait(RCC_FLAG_LSIRDY);
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);                                /* 把RTC 时钟源配置为LSI */
    } else if (clock == RTC_CLOCK_LSE) {                                       /* 当使用LSE 最为RTC 时钟源*/
        RCC_LSEConfig(RCC_LSE_ON);                                             /* 使能LSE 振荡*/
        RCC_LSEDriveConfig(RCC_LSEDrive_High);
        RCC_ConfigWait(RCC_FLAG_LSERDY);
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);                                /* 把RTC 时钟源配置为使用LSE */
    }
    
    RTC_WaitForSynchro();
    RCC_RTCCLKCmd(ENABLE);                                                     /* 使能RTC 时钟*/
    RTC_WaitForSynchro();
    return TRUE;
}

/**************************************************************************************************
**  函数名称:  RTC_RtcConfig
**  功能描述:  RTC参数配置
**  输入参数:  clock:  时钟源, 见 RTC_CLOCK_E
**  输出参数:  无
**  返回参数:  成功返回TRUE,失败返回FALSE
**************************************************************************************************/
static BOOLEAN RTC_RtcConfig(RTC_CLOCK_E clock)
{
    INT32U synprediv, asynprediv;
    RTC_InitTypeDef RTC_InitStruct;
    
    if (clock == RTC_CLOCK_LSI) {                                              /* 当使用LSI 作为RTC 时钟源*/
        synprediv  = 400 - 1;                                                  /* 同步分频值和异步分频值*/
        asynprediv = 100 - 1;                                                  /* 40000HZ / (400 * 100) = 1HZ */
    } else if (clock == RTC_CLOCK_LSE) {                                       /* 当使用LSE 最为RTC 时钟源*/
        synprediv  = 256 - 1;                                                  /* 同步分频值和异步分频值*/
        asynprediv = 128 - 1;                                                  /* 32768HZ/(256 * 128) =1HZ */
    } else {
        synprediv  = 2500 - 1;                                                 /* 同步分频值和异步分频值*/
        asynprediv = 100 - 1;                                                  /* 32768HZ/(256 * 128) =1HZ */
    }
    
    RTC_InitStruct.RTC_HourFormat   = RTC_HourFormat_24;
    RTC_InitStruct.RTC_AsynchPrediv = asynprediv;
    RTC_InitStruct.RTC_SynchPrediv  = synprediv;
    
    if (RTC_Init(&RTC_InitStruct) == SUCCESS) {
        RTC_WaitForSynchro();
        RTC_WriteBackupRegister(RTC_BKP_DR0, RTC_CONFIG);
        return true;
    } else {
        return false;
    }
}

/**************************************************************************************************
**  函数名称:  RTC_SetSystime
**  功能描述:  设置系统时间
**  输入参数:  time: 系统时间；wday: 星期，1-7
**  输出参数:  无
**  返回参数:  成功返回TRUE，失败返回FALSE
**************************************************************************************************/
static BOOLEAN RTC_SetSystime(SYSTIME_T *time, INT8U wday)
{
    ErrorStatus       result;
    RTC_TimeTypeDef   RTC_TimeStruct;
    RTC_DateTypeDef   RTC_DateStruct;
    
    RTC_TimeStruct.RTC_Hours   = time->time.hour;
    RTC_TimeStruct.RTC_Minutes = time->time.minute;
    RTC_TimeStruct.RTC_Seconds = time->time.second;
    
    if (time->time.hour < 12) {
        RTC_TimeStruct.RTC_H12 = RTC_H12_AM;
    } else {
        RTC_TimeStruct.RTC_H12 = RTC_H12_PM;
    }
    
    result = RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);                     /* 配置TIME */
    
    if (result != SUCCESS) {
        return false;
    }
    
    RTC_DateStruct.RTC_WeekDay = wday;
    RTC_DateStruct.RTC_Month   = time->date.month;
    RTC_DateStruct.RTC_Date    = time->date.day;
    RTC_DateStruct.RTC_Year    = time->date.year;
    
    result = RTC_SetDate(RTC_Format_BIN, &RTC_DateStruct);                     /* 配置DATE */
    
    if (result != SUCCESS) {
        return false;
    }
    
    RTC_WriteBackupRegister(RTC_BKP_DR1, RTC_CONFIG);                          /* 标记RTC已经配置成功 */
    
    return true;
}

/**************************************************************************************************
**  函数名称:  RTC_GetSystime
**  功能描述:  获取系统时间
**  输入参数:  time: 系统时间；wday: 星期，1-7
**  输出参数:  无
**  返回参数:  成功返回TRUE，失败返回FALSE
**************************************************************************************************/
static BOOLEAN RTC_GetSystime(SYSTIME_T *time, INT8U *wday)
{
    RTC_TimeTypeDef RTC_TimeStruct;
    RTC_DateTypeDef RTC_DateStruct;
        
    RTC_GetTime(RTC_Format_BIN, &RTC_TimeStruct);
    RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
    
    time->date.year   = RTC_DateStruct.RTC_Year; 
    time->date.month  = RTC_DateStruct.RTC_Month;
    time->date.day    = RTC_DateStruct.RTC_Date;
    time->time.hour   = RTC_TimeStruct.RTC_Hours;
    time->time.minute = RTC_TimeStruct.RTC_Minutes;
    time->time.second = RTC_TimeStruct.RTC_Seconds;
    
    *wday = RTC_DateStruct.RTC_WeekDay;
    
    return true;
}

/**************************************************************************************************
**  函数名称:  RTC_Config_Alarm
**  功能描述:  RTC闹钟初始化配置
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
static void RTC_Config_Alarm(void)
{
    EXTI_InitTypeDef  EXTI_InitStructure;
    
    EXTI_ClearITPendingBit(EXTI_Line17);                                       /* Configure EXTI line 17 */
    EXTI_InitStructure.EXTI_Line    = EXTI_Line17;
    EXTI_InitStructure.EXTI_Mode    = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    BSP_InstallIRQHandler(RTC_IRQn, (IRQ_SERVICE_FUNC)DAL_RTC_IRQHandler);
    BSP_ConfigIRQEnable(RTC_IRQn, ENABLE);
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_Init
**  功能描述:  初始化模块
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void DAL_RTC_Init(void)
{
    YX_MEMSET(&s_rtcctrl, 0, sizeof(s_rtcctrl));
    
    if (RTC_ReadBackupRegister(RTC_BKP_DR0) != RTC_CONFIG) {                   /* 未开启实时时钟功能 */
        s_rtcctrl.batnormal = FALSE;
    } else {
        s_rtcctrl.batnormal = TRUE;
    }

    DAL_RTC_OpenService(RTC_CLOCK_LSE);
    RTC_Config_Alarm();
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_OpenService
**  功能描述:  打开RTC服务
**  输入参数:  clock: 时钟源, 见 RTC_CLOCK_E
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
BOOLEAN DAL_RTC_OpenService(RTC_CLOCK_E clock)
{
    BOOLEAN result;

    if (RTC_ReadBackupRegister(RTC_BKP_DR0) == RTC_CONFIG) {                   /* 已经开启RTC服务 */
        
        if (s_rtcctrl.inservice == FALSE) {
            s_rtcctrl.inservice = TRUE;
            
            RTC_PwrConfig();
            if (clock == RTC_CLOCK_LSI) {
                RCC_LSICmd(ENABLE);                                            /* 使能LSI 振荡*/
            }
            
            RTC_WaitForSynchro();                                              /* 等待寄存器完成操作 */
        }
        
        return TRUE;
    }
    
    RTC_PwrConfig();
    RTC_DeInit();                                                              /* 恢复RTC寄存器 */
    RCC_BackupResetCmd(ENABLE);                                                /* 复位备份区和时钟配置 */
    RCC_BackupResetCmd(DISABLE);                                               /* 不复位 */
    
    result = RTC_ClockConfig(clock);
    
    if (result == TRUE) {
        s_rtcctrl.inservice = TRUE;
        result = RTC_RtcConfig(clock);
    }
    
    return result;
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_CloseService
**  功能描述:  关闭RTC服务
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void DAL_RTC_CloseService(void)
{
    s_rtcctrl.inservice = FALSE;
    
    if (RTC_ReadBackupRegister(RTC_BKP_DR0) == 0x00000000) {                   /* 服务已经关闭 */
        return;
    }
    
    RTC_PwrConfig();
    RTC_DeInit();                                                              /* 恢复RTC寄存器 */
    RCC_BackupResetCmd(ENABLE);                                                /* 恢复备份区和时钟配置 */
    RCC_BackupResetCmd(DISABLE);                                               /* 不复位 */
    PWR_BackupAccessCmd(DISABLE);                                              /* 关闭RTC和备份域寄存器写访问 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, DISABLE);                       /* 关闭PWR时钟*/
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_SetSystime
**  功能描述:  设置系统时间
**  输入参数:  time: 系统时间；wday: 星期，1-7
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
BOOLEAN DAL_RTC_SetSystime(SYSTIME_T *time, INT8U wday)
{
    DAL_RTC_OpenService(RTC_CLOCK_LSE);

    if (RTC_ReadBackupRegister(RTC_BKP_DR0) == RTC_CONFIG) {                   /* 服务已经开启 */
        return RTC_SetSystime(time, wday);
    } else {                                                                   /* 服务尚未开启则返回失败 */
        return FALSE;
    }
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_GetSystime
**  功能描述:  获取系统时间
**  输入参数:  time: 系统时间；wday: 星期，1-7
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
BOOLEAN DAL_RTC_GetSystime(SYSTIME_T *time, INT8U *wday)
{
    if (RTC_ReadBackupRegister(RTC_BKP_DR0) != RTC_CONFIG) {                   /* 未开启实时时钟功能 */
        return FALSE;
    }
    
    if (RTC_ReadBackupRegister(RTC_BKP_DR1) != RTC_CONFIG) {                   /* 未配置实时时钟 */
        return FALSE;
    }
    
    return RTC_GetSystime(time, wday);
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_BatIsNormal
**  功能描述:  获取RTC电池状态
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
BOOLEAN DAL_RTC_BatIsNormal(void)
{
    return s_rtcctrl.batnormal;
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_SetAlarm
**  功能描述:  设置RTC唤醒闹钟
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void DAL_RTC_SetAlarm(SYSTIME_T *time)
{
    RTC_AlarmTypeDef  RTC_AlarmStructure;
    
    RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
    
    RTC_AlarmStructure.RTC_AlarmTime.RTC_H12     = RTC_H12_AM;
    RTC_AlarmStructure.RTC_AlarmTime.RTC_Hours   = time->time.hour;
    RTC_AlarmStructure.RTC_AlarmTime.RTC_Minutes = time->time.minute;
    RTC_AlarmStructure.RTC_AlarmTime.RTC_Seconds = time->time.second;
    RTC_AlarmStructure.RTC_AlarmDateWeekDaySel   = RTC_AlarmDateWeekDaySel_Date;
    RTC_AlarmStructure.RTC_AlarmDateWeekDay      = RTC_Weekday_Monday;    
    RTC_AlarmStructure.RTC_AlarmMask             = RTC_AlarmMask_DateWeekDay;
    
    RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &RTC_AlarmStructure);
}

/**************************************************************************************************
**  函数名称:  DAL_RTC_ConfigAlarm
**  功能描述:  配置闹钟功能（开启或者关闭）
**  输入参数:  无
**  输出参数:  无
**  返回参数:  无
**************************************************************************************************/
void DAL_RTC_ConfigAlarm(BOOLEAN enable)
{
    if (enable == TRUE) {
        RTC_ITConfig(RTC_IT_ALRA, ENABLE);
        RTC_AlarmCmd(RTC_Alarm_A, ENABLE);
        RTC_ClearFlag(RTC_FLAG_ALRAF);
        PWR_ClearFlag(PWR_FLAG_WU);
        RTC_ClearITPendingBit(RTC_IT_ALRA);
        EXTI_ClearITPendingBit(EXTI_Line17);
    } else {
        RTC_ITConfig(RTC_IT_ALRA, DISABLE);
        RTC_AlarmCmd(RTC_Alarm_A, DISABLE);
    }
}

